package com.leyou.utils;

/**
 * @author jshstart
 * @creat 2020-12-23 9:36
 */

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.leyou.constants.JwtConstants;
import com.leyou.constants.RedisConstants;
import com.leyou.dto.Payload;
import com.leyou.dto.UserDetails;
import com.leyou.myException.LyException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.crypto.SecretKey;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * jwt工具类，生成jwt，解析验证jwt
 */
public class  JwtUtil {
    //jwt解析器
    private final JwtParser jwtParser;
    //密匙
    private final SecretKey secretKey;
   private final static ObjectMapper MAPPER= new ObjectMapper();
   @Autowired
   private StringRedisTemplate redisTemplate;
    public JwtUtil(String key) {
        secretKey=Keys.hmacShaKeyFor(key.getBytes(Charset.forName("UTF-8")));
       this.jwtParser=Jwts.parserBuilder().setSigningKey(secretKey).build();
    }
    public String createJwt(UserDetails userDetails){
        return createJwt(userDetails,1800);
    }

    /**
     * 生成jwt
     * @param userDetails 用户信息
     * @param expireSeconds 指定过期的时间
     * @return
     */
    public String createJwt(UserDetails userDetails,int expireSeconds){
        try {
            String jti = createJti();
            String userJwt = Jwts.builder().signWith(secretKey)
                    .setId(jti)
                    .claim("user", MAPPER.writeValueAsString(userDetails))
                    .setExpiration(DateTime.now().plusSeconds(expireSeconds).toDate())
                    .compact();
            //根据jwt的id把用户jwt存放到redis中
            redisTemplate.opsForValue().set(RedisConstants.JTI_KEY_PREFIX+userDetails.getId(),jti,expireSeconds, TimeUnit.SECONDS);
            return userJwt;
        } catch (JsonProcessingException e) {
           throw new RuntimeException(e);
        }
    }

    /**
     * 验证解析用户传送过来的jwt，并把用户信息返回
     * @param jwt
     * @return
     */
    public Payload parseJwt(String jwt){
        try {
            //验证并解析jwt
        Jws<Claims> claimsJws = jwtParser.parseClaimsJws(jwt);
        Claims claims = claimsJws.getBody();
        //解析载荷数据
        Payload payload = new Payload();
        payload.setJti(claims.getId());
        UserDetails userDetails = MAPPER.readValue(claims.get("user", String.class), UserDetails.class);
        payload.setUserDetail(userDetails);
        //验证是否过期
            //1.取出redis中的jti
            String redisJti = redisTemplate.opsForValue().get(RedisConstants.JTI_KEY_PREFIX + userDetails.getId());
            //2.获取jwt的jti
            String payJti = payload.getJti();
            //3.对比
            if (redisJti==null){
                throw new RuntimeException("登陆过期，请重新登里");
            }
            if (!redisJti.equals(payJti)){
                throw new RuntimeException("用户在其他地方登录，请及时修改密码");
            }

        return payload;
        }catch (IllegalArgumentException e) {
            throw new LyException(401, "用户未登录！");
        }catch (JwtException e) {
            throw new LyException(401, "登录无效或者已经超时！");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    private String createJti() {
        return StringUtils.replace(UUID.randomUUID().toString(), "-", "");
    }

    /**
     * 用来刷新jti
     * @param id
     */
    public void refreshJwt(Long id) {
        refresh(id,RedisConstants.TOKEN_EXPIRE_SECONDS);
    }
    public void refresh(Long id,int expireSeconds) {
        redisTemplate.expire(RedisConstants.JTI_KEY_PREFIX+id,expireSeconds,TimeUnit.SECONDS);
    }
}
